<?php

/**
 * 版本命名规则：大版本1.0.1
 *               小版本1.0.1.06280 热更新
 *
 * 主版本.子版本.阶段版本.日期小版号（热更版本）
 * 优先更新大版本
 * 更新的目录结构
 *
 * ├─update                更新目录
 * │  ├─zzd                   转圈圈的更新目录
 * │  │  ├─1.0.1.wgt             1.0.1大版本更新
 * │  │  ├─1.0.1                 1.0.1小版本更新文件
 * │  │     ├─1.0.1.08010.wgtu        热更版本1
 * │  │     ├─1.0.1.08011.wgtu        热更版本2
 * │  │  ├─1.0.2.wgt             1.0.2大版本更新
 * │  │  ├─1.0.2                 1.0.2小版本更新文件
 * │  │     ├─1.0.2.08010.wgtu        热更版本1
 * │  │     ├─1.0.2.08011.wgtu        热更版本2
 * │  ├─ryb                    如意宝的更新目录
 * │  │  ├─1.0.1.wgt            1.0.1大版本更新
 * │  │  ├─1.0.1                1.0.1小版本更新文件
 * │  │     ├─1.0.1.08010.wgtu        热更版本1
 * │  │     ├─1.0.1.08011.wgtu        热更版本2
 * │  │  ├─1.0.2.wgt            1.0.2大版本更新
 * │  │  ├─1.0.2                1.0.2小版本更新文件
 * │  │     ├─1.0.2.08010.wgtu        热更版本1
 * │  │     ├─1.0.2.08011.wgtu        热更版本2
 */
class libUpdate
{
    //定义标志文件夹
    public $identifyArray = ["zzd", "ryb", "xl", "jf", "test"];
    private $_filename = '';
    //定义热更文件夹目录
    private $_updateUrl = "../update/";
    //下载的cnd路径地址
    private $_cdn = "http://www.zhuanliuliangwang.com.cn/update";


    //加密key
    const CRYPT_AUTH_KEY = "4Dd3msLRO*e!e#%o";
    private $_value = NULL;

    public function __construct()
    {
        ini_set("display_errors", 0);
        error_reporting(E_ALL ^ E_NOTICE);
        error_reporting(E_ALL ^ E_WARNING);
    }

    /**
     * 系统加密方法
     * @param        $data :要加密的字符串
     * @param string $key :加密密钥
     * @param int    $expire :过期时间 单位 秒（用于redis等缓存中）
     * @return mixed
     */
    private function _encrypt($data, $key = '', $expire = 0)
    {
        $key = md5(empty($key) ? self::CRYPT_AUTH_KEY : $key);
        $data = base64_encode($data);
        $x = 0;
        $len = strlen($data);
        $l = strlen($key);
        $char = '';
        for($i = 0; $i < $len; $i++){
            if($x == $l){
                $x = 0;
            }
            $char .= substr($key, $x, 1);
            $x++;
        }
        $str = sprintf('%010d', $expire ? $expire + time() : 0);
        for($i = 0; $i < $len; $i++){
            $str .= chr(ord(substr($data, $i, 1)) + (ord(substr($char, $i, 1))) % 256);
        }
        return str_replace(array('+', '/', '='), array('-', '_', ''), base64_encode($str));
    }

    /**
     * 系统解密方法
     * @param        $data :要解密的字符串
     * @param string $key :加密密钥
     * @return bool|string
     */
    private function _decrypt($data, $key = '')
    {
        $key = md5(empty($key) ? self::CRYPT_AUTH_KEY : $key);
        $data = str_replace(array('-', '_'), array('+', '/'), $data);
        $mod4 = strlen($data) % 4;
        if($mod4){
            $data .= substr('====', $mod4);
        }
        $data = base64_decode($data);
        $expire = substr($data, 0, 10);
        $data = substr($data, 10);
        if($expire > 0 && $expire < time()){
            return '';//过期返回空
        }
        $x = 0;
        $len = strlen($data);
        $l = strlen($key);
        $char = $str = '';
        for($i = 0; $i < $len; $i++){
            if($x == $l){
                $x = 0;
            }
            $char .= substr($key, $x, 1);
            $x++;
        }
        for($i = 0; $i < $len; $i++){
            if(ord(substr($data, $i, 1)) < ord(substr($char, $i, 1))){
                $str .= chr((ord(substr($data, $i, 1)) + 256) - ord(substr($char, $i, 1)));
            } else{
                $str .= chr(ord(substr($data, $i, 1)) - ord(substr($char, $i, 1)));
            }
        }
        return base64_decode($str);
    }

    /**
     * 版本检测
     * @param     $nowVersion :当前版本
     * @param     $path：路径
     * @param     $identify：更新标志
     * @param int $isBigVersion：是否是大版本更新
     * @return array|bool|int
     */
    private function _checkVersion($nowVersion, $path, $identify, $isBigVersion = 1)
    {
        //按字母降序排列
        try{
            $file = @scandir($path, 0);
            $downInfo = [];
            // $downInfo['path'] = ['1','2'];
            $pathList = [];
            foreach($file as $key => $v){
                if(preg_match("/(.*)\.wgtu?$/is", $v, $match)){
                    $serviceVersion = $match[1];
                    if(version_compare($nowVersion, $serviceVersion, "<")){
                        $this->_filename = $v;

                        //是否是大版本更新
                        if($isBigVersion){
                            $downInfo['url'] = sprintf("%s/%s/%s", $this->_cdn, $identify, $v);
                        } else{
                            $downInfo['url'] = sprintf("%s/%s/%s/%s", $this->_cdn, $identify, $nowVersion, $v);
                        }
                        $tempPath = sprintf("%s%s", $path, $v);
                        $downInfo['path'][] = $tempPath;
                        // $downInfo['path'] = sprintf("%s%s", $path, $v);
                        // var_dump($downInfo);
                        //返回下载地址和文件路径
                        // return $downInfo;
                    }
                }
            }
            // return FALSE;
            return $downInfo;
        } catch(Exception $e){
            return -1;
        }
    }

    //获取版本的差量
    private function _getVersionDirToSmallVersion($nowVersion)
    {
        $nowVersionDir = explode(".", $nowVersion);
        return sprintf("%s.%s.%s", $nowVersionDir[0], $nowVersionDir[1], $nowVersionDir[2]);
    }

    //下载文件
    private function _downloadFile($uploaderUrl, $filePath, $allowExt = ['wgt', 'wgtu'])
    {
        //检测下载文件是否存在 并且可读
        if(!is_file($filePath) && !is_readable($filePath)){
            return FALSE;
        }
        //检测文件类型是否允许下载
        $ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
        if(!in_array($ext, $allowExt)){
            return FALSE;
        }
        //判断如果文件存在,则跳转到下载路径
        if(file_exists($filePath)){
        	$uploaderUrl = str_replace("../",$_SERVER['HTTP_HOST'] ."/",$filePath);
            header("location:$filePath");
        } else{
            return FALSE;
        }
        return FALSE;
    }

    /* 参数获取 */
    private function _removeEmoji($text)
    {
        // 匹配4字节的unicode
        $regexEmoticons = '/[\x{10000}-\x{10FFFF}]/u';
        $text = preg_replace($regexEmoticons, '', $text);
        return $text;
    }

    //过滤特殊字符
    private function _filterSpecialChars($str, $isEmoji = TRUE)
    {

        //删除零宽度空格符(zero-width-space)
        $str = str_replace("\xe2\x80\x8b", '', $str);
        $str = str_replace("\xe2\x80\x8c", '', $str);
        $str = str_replace("\xe2\x80\x8d", '', $str);
        $str = str_replace("\xe2\x80\xac", '', $str);
        $str = str_replace("\xe2\x80\xad", '', $str);

        //移除emojo表情
        if($isEmoji){
            $str = $this->_removeEmoji($str);
        }

        return $str;
    }

    /**
     * @param bool   $trim
     * @param string $default
     * @param null   $length
     * @param null   $start
     * @return mixed|null|string
     */
    private function _toString($default = '', $length = NULL, $start = NULL, $isEmoji = TRUE)
    {
        if(is_null($this->_value)){
            return $default;
        }
        $value = $this->_filterSpecialChars($this->_value, $isEmoji);
        if($length === NULL && $start === NULL){
            return $value;
        } elseif($start === NULL){
            return substr($value, 0, $length);
        } elseif($length === NULL){
            return substr($value, $start);
        } else{
            return substr($value, $start, $length);
        }
    }
    /**
     * @param string $name
     * @param bool   $trim
     * @param null   $default
     * @param string $filter
     * @return string
     */
    public function get($name = '', $trim = TRUE)
    {
        $this->_value = $_GET[$name];
        $string = $this->_toString("");
        $string = trim(htmlspecialchars($string, ENT_QUOTES));
        return $string;
    }

    //输出json
    public function json($array)
    {
        header('Content-Type:application/json; charset=utf-8');
        $original = json_encode($array, JSON_UNESCAPED_UNICODE);
        echo $original;
        exit();
    }

    //加密下载路径
    public function encrypt($string)
    {
        return $this->_encrypt($string);
    }

    //解密下载路径
    public function decrypt($string)
    {
        $downInfo = $this->_decrypt($string);
        $downInfo = json_decode($downInfo, 1);
        if(!is_array($downInfo)){
            return FALSE;
        }

        return $downInfo;
    }

    //下载文件
    public function downLoadFile($uploaderUrl, $path)
    {
        $this->_downloadFile($uploaderUrl, $path);
        return FALSE;
    }

    //获取文件名称
    public function getFileName()
    {
        return $this->_filename;
    }

    //app更新检测
    public function checkAppUpdate($nowVersion, $identify)
    {
        //校验版本
        $path = sprintf("%s%s/", $this->_updateUrl, $identify);
        $downloadFile = $this->_checkVersion($nowVersion, $path, $identify, 1);
        if($downloadFile == -1){
            return -1;
        }
        //如果有大版本就更新大版本
        if($downloadFile){
            $arr['url'] = $downloadFile['url'];
            $arr['path'] = $downloadFile['path'];
            $string = json_encode($arr, JSON_UNESCAPED_UNICODE);
            return $this->encrypt($string);
        }
        //检测小版本进行差量更新（版本文件夹里建立对应的大版本文件夹，放小版本文件）
        $nowVersionDir = $this->_getVersionDirToSmallVersion($nowVersion);
        $smallPath = sprintf("%s%s/%s/", $this->_updateUrl, $identify, $nowVersionDir);
        $downloadFile = $this->_checkVersion($nowVersion, $smallPath, $identify, 0);
        if($downloadFile == -1){
            return -1;
        }

        if($downloadFile){
            $arr['url'] = $downloadFile['url'];
            $arr['path'] = $downloadFile['path'];
            $string = json_encode($arr, JSON_UNESCAPED_UNICODE);
            return [
            	'data'		=> $this->encrypt($string),
            	'filenum'	=> count($arr['path']),
            ];
        }
        return TRUE;
    }
}

?>
